﻿using System;
using System.Diagnostics;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Windows.Forms;

using BreukTrainer.BreukModel;

namespace BreukControls
{
    public delegate void FoutiefGebruikEventHandler(BreukRekenMachine sender, FoutEvent e);

    [System.ComponentModel.DefaultEvent("FoutiefGebruik")]
    public partial class BreukRekenMachine : UserControl
    {
        public event FoutiefGebruikEventHandler FoutiefGebruik;

        private bool reset = false;

        private bool syntaxError = false;

        private Breuk result;

        //Dit is om te testen of er fouten zijn in de berekening
        public bool SyntaxError
        {
            get
            {
                return syntaxError;
            }
            set
            {
                syntaxError = value;
            }
        }

        //kan handig zijn bij het gebruik van de control
        //(om bv de achtergrondkleur rood te maken bij een fout ofzo)
        public TextBox TextBerekening
        {
            get
            {
                return textBerekening;
            }
        }

        public Breuk Result
        {
            get
            {
                return result;
            }
            set
            {
                result = value;
            }
        }

        public BreukRekenMachine()
        {
            InitializeComponent();
            this.button1.Click += new System.EventHandler(this.button_Click);
            this.button2.Click += new System.EventHandler(this.button_Click);
            this.button3.Click += new System.EventHandler(this.button_Click);
            this.button4.Click += new System.EventHandler(this.button_Click);
            this.button5.Click += new System.EventHandler(this.button_Click);
            this.button6.Click += new System.EventHandler(this.button_Click);
            this.button7.Click += new System.EventHandler(this.button_Click);
            this.button8.Click += new System.EventHandler(this.button_Click);
            this.button9.Click += new System.EventHandler(this.button_Click);
            this.button0.Click += new System.EventHandler(this.button_Click);
            this.buttonBreuk.Click += new System.EventHandler(this.button_Click);
            this.buttonPlus.Click += new System.EventHandler(this.button_Click);
            this.buttonMin.Click += new System.EventHandler(this.button_Click);
            this.buttonMaal.Click += new System.EventHandler(this.button_Click);
            this.buttonDeel.Click += new System.EventHandler(this.button_Click);
            this.buttonResultaat.Click += new System.EventHandler(this.buttonResultaat_Click);
            this.FoutiefGebruik += delegate(BreukRekenMachine rek, FoutEvent e)
                                   {
                                       Debug.WriteLine(e.Message);
                                   };
        }

        private void button_Click(object sender, EventArgs e)
        {
            if (reset)
            {
                textBerekening.Text = "";
                reset = false;
            }
            Button btn = (Button)sender;
            textBerekening.AppendText(btn.Text);
            textBerekening.Focus();
        }

        private void buttonResultaat_Click(object sender, EventArgs e)
        {
            string berekening = textBerekening.Text;
            textBerekening.Text = Bereken(berekening);
            reset = true;
        }

        private void textBerekening_TextChanged(object sender, EventArgs e)
        {
            TextBox tb = (TextBox)sender;
            String berekening = tb.Text;
            ValidateBerekening(berekening);
        }

        protected virtual void OnFout(FoutEvent e)
        {
            if (FoutiefGebruik != null)
            {
                FoutiefGebruik(this, e);
            }
        }

        private void ValidateBerekening(string berekening)
        {
            //dit moet omdat hij anders nooit op false gezet zal worden
            SyntaxError = false;

            if (berekening.Length == 0)
            {
                //als er geen text is moeten we het niet testes
                //en als we het wel doen krijgen we problemen
                SyntaxError = false;//technisch gezien is een lege berekening geen foute
                return;
            }
            
            //regex om op de operanden te splitten
            Regex operanden = new Regex("(\\+|-|\\*|:)");// + en * hebben speciale betekenissen in reguliere expressies, daarom zijn ze ge escaped (\\)
            
            //regex om breuken te controleren
            Regex breuk = new Regex("^-?\\d+(/([1-9]|\\d{2,}))?$");
            
            //regex om te contorlleren of er meerdere operanden na elkaar voorkomen
            Regex meerdereoperanden = new Regex("(\\+|-|\\*|:){2,}");
            Match meerdere = meerdereoperanden.Match(berekening);
            if (meerdere.Success)
            {
                FoutEvent e = new FoutEvent("Er werden meerdere operanden na elkaar ingevoerd op positie " + (meerdere.Index + 1) + " van berekening " + berekening);
                OnFout(e);
                SyntaxError = true;
            }
            
            //regex om te checken of er operanden op onmogelijke plaatsen zijn (in het begin of op het einde) 
            Regex onmogelijkeoperanden = new Regex("(^(\\+|-|\\*|:)+.)|(.(\\+|-|\\*|:)$)");
            Match onmogelijk = onmogelijkeoperanden.Match(berekening);
            if (onmogelijk.Success)
            {
                FoutEvent e = new FoutEvent("Er werden een operand ingevoerd op een onmogelijke plaats op positie " + (onmogelijk.Index + 1) + " van berekening " + berekening);
                OnFout(e);
                SyntaxError = true;
            }
            string[] breuken = operanden.Split(berekening);
            int pos = 1;
            foreach(string s in breuken)
            {
                //de Regex.Split() methode geeft ook de waardes waarop er gesplits werd terug dus moeten we ze negeren
                //soms komen er lege string in de resultaat terwijl dit niet echt en fout is dus die negeren wij ook
                if (!breuk.IsMatch(s) && !operanden.IsMatch(s) && s != "")
                {
                    FoutEvent e = new FoutEvent("Er werd een breuk verwacht op posisie " + pos + " [" + s + "] werd gevonden");
                    OnFout(e);
                    SyntaxError = true;
                }
                pos += s.Length;
            }
        }

        private string Bereken(string berekening)
        {
            ValidateBerekening(berekening);
            if (!SyntaxError)
            {
                Regex regOperanden = new Regex("(\\+|-|\\*|:)");
                Regex regBreuk = new Regex("^-?\\d+(/([1-9]|\\d{2,}))?$");

                ProgrammeerbareBreukContainer container = new ProgrammeerbareBreukContainer();
                List<string> operanden = new List<string>();
                List<DelegateBreukenBewerking> bewerkingen = new List<DelegateBreukenBewerking>();

                string[] operands = regOperanden.Split(berekening);
                foreach (string s in operands)
                {
                    try
                    {
                        container.Add(Breuk.Parse(s));
                        Debug.Write("Breuk:  ");//test
                        Debug.WriteLine(s);
                    }
                    catch (ArgumentException ex)
                    {
                        if (regOperanden.IsMatch(s))
                        {
                            Debug.Write("Operand:");//test
                            Debug.WriteLine(s);//test
                            operanden.Add(s);
                            switch(s)
                            {
                                case "+": 
                                    bewerkingen.Add((b1, b2) => b1 + b2);
                                    break;
                                case "-":
                                    bewerkingen.Add((b1, b2) => b1 - b2);
                                    break;
                                case "*":
                                    bewerkingen.Add((b1, b2) => b1 * b2);
                                    break;
                                case ":":
                                    bewerkingen.Add((b1, b2) => b1 / b2);
                                    break;
                                default:
                                    break;
                            }
                        }
                        else
                        {
                            Debug.Write("Andere: ");
                            Debug.WriteLine(s);
                        }                        
                    }
                }
                Result = Breuk.Parse(container.DoeBewerkingResult(bewerkingen.ToArray(), operanden.ToArray()));
                return Result.ToString();
            }else{
                //Er is een fout, doe niets
                return "syntax error";//dit lijkt me toepasselijker
            }
        }

        private void buttonUndo_Click(object sender, EventArgs e)
        {
            int len = textBerekening.Text.Length;
            if (len >= 1)
            {
                textBerekening.Text = textBerekening.Text.Substring(0, len - 1);
                textBerekening.Focus();
            }
        }

        private void buttonClear_Click(object sender, EventArgs e)
        {
            textBerekening.Text = "";
        }

        private void buttonNotatie_Click(object sender, EventArgs e)
        {
            try
            {
                Breuk b = Breuk.Parse(textBerekening.Text);
                textBerekening.Text = b.DecimaleWaarde().ToString();
            }
            catch (ArgumentException ex)
            {
                if(Result != null)
                    textBerekening.Text = Result.ToString();
            }
        }
    }
}
